package xpn.platform.modules.workflow.common;

import com.google.common.reflect.TypeToken;
import com.google.gson.*;
import com.innahema.collections.query.queriables.Queryable;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;
import xpn.platform.common.config.XpnPlatformConfig;
import xpn.platform.common.exception.XpnBusinessException;
import xpn.platform.common.exception.XpnExceptionCodeEnum;
import xpn.platform.common.rulesengine.SimpleRulesEngine;
import xpn.platform.common.util.Tool;
import xpn.platform.modules.sys.permission.CheckPermissionRequest;
import xpn.platform.modules.sys.permission.CheckPermissionRequest.InjectedEntity;
import xpn.platform.modules.sys.permission.CheckPermissionRequest.Rule;
import xpn.platform.modules.workflow.common.dto.UserDto;
import xpn.platform.modules.workflow.definition.action.*;
import xpn.platform.modules.workflow.definition.process.Process;
import xpn.platform.modules.workflow.definition.process.ProcessDAO;
import xpn.platform.modules.workflow.definition.state.State;
import xpn.platform.modules.workflow.definition.state.StateDAO;
import xpn.platform.modules.workflow.other.comment.Comment;
import xpn.platform.modules.workflow.other.comment.CommentDAO;
import xpn.platform.modules.workflow.other.commentcolumn.CommentColumnDAO;
import xpn.platform.modules.workflow.runtime.history.History;
import xpn.platform.modules.workflow.runtime.history.HistoryDAO;
import xpn.platform.modules.workflow.runtime.instance.Instance;
import xpn.platform.modules.workflow.runtime.instance.InstanceDAO;
import xpn.platform.modules.workflow.runtime.todo.Todo;
import xpn.platform.modules.workflow.runtime.todo.TodoDAO;
import xpn.platform.modules.workflow.runtime.todo.TodoDTO;

import java.net.URI;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class WorkflowServiceImpl implements WorkflowService {

    @Autowired
    Gson gson;

    @Autowired
    XpnPlatformConfig xpnPlatformConfig;

    @Autowired
    private ProcessDAO processDao;

    @Autowired
    private InstanceDAO instanceDao;

    @Autowired
    private HistoryDAO historyDao;

    @Autowired
    private TodoDAO todoDao;

    @Autowired
    private StateDAO stateDao;

    @Autowired
    private ActionDAO actionDao;

    @Autowired
    private CommentDAO commentDAO;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private CommentColumnDAO commentColumnDAO;

    @Autowired
    private SimpleRulesEngine simpleRulesEngine;

    private String goBackString = "退回";

    @Override
    @Transactional(rollbackFor = Exception.class)
    public TodoDTO startProcess(Long id, String name, String no, String starterId, String tokenName,
                                String token, String entityLink, String refVarName,
                                String parentInstanceId,String parentInstanceEntityLink) {
        Todo todo = null;
        if (no != null && !"".equals(no)) {
            todo = startProcessByNo(no, starterId, tokenName, token, entityLink, refVarName,
                    parentInstanceId,parentInstanceEntityLink);
        } else if (name != null && !"".equals(name)) {
            todo = startProcessByName(name, starterId, tokenName, token, entityLink, refVarName,
                    parentInstanceId,parentInstanceEntityLink);
        } else if (id != null) {
            todo = startProcessById(id, starterId, tokenName, token, entityLink, refVarName,
                    parentInstanceId,parentInstanceEntityLink);
        }
        if (todo == null) {
            return null;
        }

        TodoDTO dto = new TodoDTO();
        dto.setTodo(todo);

        // 检查操作权限，重新设置操作
        List<Action> actions = checkPermissionOfActions(actionDao.findByStateOrderBySortNo(todo.getState()), entityLink, refVarName, tokenName,
                token, starterId);
        dto.setActions(actions);

        // 设置意见栏和意见列表
        dto.setCommentColumns(commentColumnDAO.findByProcessIdOrderBySortNoDesc(todo.getInstance().getProcess().getId()));

        //设置原始作者和本人的发送人
        dto.setStarterId(starterId);
        return dto;
    }

    @Transactional(rollbackFor = Exception.class)
    public Todo startProcessByName(String name, String starterId, String tokenName,
                                   String token, String entityLink, String refVarName,
                                   String parentInstanceId,String parentInstanceEntityLink) {
        // 根据名称查找流程定义
        Process process = processDao.findFirstByName(name);

        Todo todo = startProcess(process, starterId, tokenName, token,
                entityLink, refVarName,parentInstanceId,parentInstanceEntityLink);

        return todo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Todo startProcessByNo(String no, String starterId, String tokenName, String token, String entityLink, String refVarName) {
        // 根据编号（流程定义Key）查找流程定义
        Process process = processDao.findFirstByNo(no);

        Todo todo = startProcess(process, starterId, tokenName, token, entityLink, refVarName);

        return todo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Todo startProcessByNo(String no, State state,String starterId, String tokenName, String token, String entityLink, String refVarName) {
        // 根据编号（流程定义Key）查找流程定义
        Process process = processDao.findFirstByNo(no);

        Todo todo = startProcess(process,state,starterId, tokenName, token, entityLink, refVarName);

        return todo;
    }

    @Transactional(rollbackFor = Exception.class)
    public Todo startProcessByNo(String no, String starterId, String tokenName,
                                 String token, String entityLink, String refVarName,
                                 String parentInstanceId,String parentInstanceEntityLink) {
        // 根据编号（流程定义Key）查找流程定义
        Process process = processDao.findFirstByNo(no);

        Todo todo = startProcess(process, starterId, tokenName, token,
                entityLink, refVarName,parentInstanceId,parentInstanceEntityLink);

        return todo;
    }

    @Override
    public Page<Todo> getTodosByInstanceId(Long instanceId, Boolean isCompleted, Pageable pageable) {
        if (isCompleted != null) {
            return todoDao.findByInstanceAndCompleted(instanceDao.getById(instanceId), isCompleted, pageable);
        }
        List<Todo> todos = todoDao.findByInstance(instanceDao.getById(instanceId));
        Page<Todo> pageNew = new PageImpl<>(todos, pageable, todos.size());
        return pageNew;
    }

    @Transactional(rollbackFor = Exception.class)
    public Todo startProcessById(Long id, String starterId, String tokenName, String token,
                                 String entityLink, String refVarName,
                                 String parentInstanceId,String parentInstanceEntityLink) {
        // 根据Id查找流程定义
        Process process = processDao.getById(id);

        Todo todo = startProcess(process, starterId, tokenName, token,
                entityLink, refVarName,parentInstanceId,parentInstanceEntityLink);

        return todo;
    }

    /**
     * 创建新流程具体实现
     *
     * @param process   流程定义
     * @param starterId 启动者Id
     * @return 创建成功的待办
     */
    private Todo startProcess(Process process, String starterId, String tokenName, String token,
                              String entityLink, String refVarName,
                              String parentInstanceId,String parentInstanceEntityLink) {
        // 检查是否具备创建流程的权限
        List<Action> actions = new ArrayList<>();
        Action action = new Action();
        action.setId(process.getId());
        action.setAuthorizationRule(process.getStartAuthorizationRule());
        actions.add(action);
        actions = checkPermissionOfActions(actions, entityLink, refVarName, tokenName, token, "");
        if (actions.isEmpty() || actions.size() == 0) {
            return null;
        }

        // 创建流程实例
        Instance instance = new Instance();
        instance.setStarterId(starterId);
        instance.setProcess(process);
        instance.setParentInstanceId(parentInstanceId);
        instance.setParentInstanceEntityLink(parentInstanceEntityLink);
        instance.setEntityName(refVarName.replaceAll("Ref",""));
        instanceDao.save(instance);

        // 创建待办
        // 执行人
        JsonObject executor = getUserObj(starterId, tokenName, token);
        Todo todo = createTodo(instance, stateDao.getById(process.getInitStateId()), null, null,
                executor, java.util.UUID.randomUUID().toString(), null, null, null, tokenName, token,null);
        todoDao.save(todo);

        return todo;
    }

    @Override
    public Page<Todo> getTodosByExecutorId(String id, boolean isCompleted, Pageable pageable) {
        Page<Todo> list = todoDao.findByExecutorIdAndCompleted(id, isCompleted, pageable);

        return list;
    }

    @Override
    public Page<Action> getActionsByTodo(Long id, Pageable pageable) {
        // 查询待办事项
        Todo todo = todoDao.getById(id);

        // 完成的待办直接返回空
        if (todo.getCompleted()) {
            return new PageImpl<Action>(new ArrayList<>());
        }

        // 根据待办事项的 state 查询相应的操作列表
        Page<Action> page = actionDao.findByState(todo.getState(), pageable);
        return page;
    }

    public Comment newComment(Todo todo, JsonObject executor,Action action, String commentContent){
        Comment comment = new Comment();
        comment.setInstance(todo.getInstance());
        comment.setUserId(todo.getExecutorId());
        comment.setUserName(executor.get("name").getAsString());
        comment.setUserOrgName(executor.get(xpnPlatformConfig.getUser().getOrgNameFild()).getAsString());
        comment.setTodo(todo);
        comment.setColumn(action.getCommentColumn());
        comment.setContent(commentContent);
        comment=commentDAO.save(comment);
        return comment;
    }

    /**
     * 构造自动退回按钮，不等待，不代表，自动发送
     * @param stateId
     * @return
     */
    private Action constructGoBackAction(Long stateId,String actionName){
        Action action = new Action();
        action.setSendeeState(stateDao.getById(stateId));
        action.setWaitMode(WaitModeEnum.NOT_WAIT);
        action.setDelegateMode(DelegateModeEnum.DELEGATE_SAME_BATCH);
        action.setActionType(ActionTypeEnum.AUTO_SEND);
        action.setName(actionName);
        return action;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Object execute(Long todoId, Long actionId, JsonArray sendees, String commentContent,
                        String tokenName, String token,Long commentActionId,String entityLink,Long stateId,String actionName) {
        try{
            // ------ 变量定义 start ------
            // 本次操作的批次号
            final String batchNumber = java.util.UUID.randomUUID().toString();

            // 待办
            final Todo todo = todoDao.getById(todoId);

            // 原始作者
            JsonObject creator = getUserObj(todo.getInstance().getStarterId(), tokenName, token);

            // 执行人
            JsonObject executor = getUserObj(todo.getExecutorId(), tokenName, token);

            // 接收人
            JsonArray sendeeList = sendees;

            // 操作
            Action action = actionDao.getById(actionId);
            //如果操作时空，那么就是自动退回操作，stateId会有值,就按照自动退回处理
            if (action==null) {
                if(stateId==null||stateId<1){
                    throw new XpnBusinessException(XpnExceptionCodeEnum.BUSINESS_EXCEPTION_HTTP_STATUS,"自动流程异常，状态id错误");
                }else{
                    //构造自动退回操作。
                    action = constructGoBackAction(stateId, actionName);
                }
            }

            //如果有父流程操作，就执行父流程操作
            if(action.getParentProcessAction()!=null){
                //先结束当前待办
                if (!action.getKeepAsExecutor()) {
                    completeTodo(todo.getId(),todo.getExecutorId(),actionId,tokenName,token);
                }
                //获取自己的父流程的待办
                Instance parenInstance=instanceDao.getById(Long.parseLong(todo.getInstance().getParentInstanceId()));
                List<Todo> parentToDos = todoDao.findByExecutorIdAndInstanceAndCompleted(todo.getExecutorId(), parenInstance, false);
                for(Todo parentToDo:parentToDos){
                    //如果这些待办中对应的action有需要执行的操作，就是要执行这个待办
                    List<Action> parentActions=actionDao.findByStateOrderBySortNo(parentToDo.getState());
                    for(Action parentAction:parentActions){
                        if(parentAction.getId().equals(action.getParentProcessAction().getId())){
                            execute(parentToDo.getId(),
                                    parentAction.getId(),
                                    sendees,
                                    commentContent,
                                    tokenName,
                                    token,
                                    commentActionId,
                                    todo.getInstance().getParentInstanceEntityLink(),null,null);
                            return true;
                        }
                    }
                }
                return true;
            }

            // 本次操作是否触发状态
            boolean isTriggerState = false;
            // ------ 变量定义 end ------

            // 执行操作逻辑如下：
            // 填写意见处理：保存意见
            Comment comment=null;
            if (action.getActionType() == ActionTypeEnum.FILL_IN_COMMENT) {
                Comment commentOld = commentDAO.findByUserAndInstanceAndTodoAndColumn(todo.getExecutorId(),
                        todo.getInstance().getId(), todo.getId(), action.getCommentColumn().getId());
                if (commentOld != null) {
                    commentOld.setContent(commentContent);
                    comment=commentDAO.save(commentOld);
                } else {
                    comment=newComment(todo,executor,action,commentContent);
                }
            }

            if(action.getActionType() != ActionTypeEnum.FILL_IN_COMMENT&&commentActionId!=null){
                Action commentAction=actionDao.getById(commentActionId);
                comment=newComment(todo,executor,commentAction,commentContent);
            }

            // 如果是自动发送或选人发送，则必然要触发新状态
            if (action.getActionType() == ActionTypeEnum.AUTO_SEND
                    || action.getActionType() == ActionTypeEnum.SELECT_PEOPLE_SEND) {
                isTriggerState = true;
            } else {
                // 判断是否触发到新状态
                if (action.getSendeeState() != null) {
                    isTriggerState = true;
                }
            }

            // 如果不触发新状态，则结束
            if (!isTriggerState) {
                return true;
            }

            // 除去选人发送、自动发送之外，接收人一概设为自己
            if (action.getActionType() != ActionTypeEnum.SELECT_PEOPLE_SEND
                    && action.getActionType() != ActionTypeEnum.AUTO_SEND) {
                sendeeList.add(executor);
            }

            //计算待办列表的标题，分类，等信息
            JsonObject entityObj=getEntityObj(entityLink,tokenName,token);
            String extendJson=todo.getInstance().getProcess().getExtendJson();
            JsonObject extendJsonObj=new JsonParser().parse(extendJson).getAsJsonObject();
            String todoTitle=getInfoInEntityObj(entityObj,extendJsonObj.get("title"));
            if(action.getTodoTip()!=null&&!action.getTodoTip().equals("")){
                JsonObject todoTip=new JsonParser().parse(action.getTodoTip()).getAsJsonObject();
                todoTitle=getInfoInEntityObj(entityObj,todoTip.get("tip"))+todoTitle;
            }
            String todoCategory=getInfoInEntityObj(entityObj,extendJsonObj.get("category"));
            String todoEmergency=extendJsonObj.get("emergency").getAsString();

            //处理时限配置  zyk 20220823
            State sendeeState = action.getSendeeState();
            Long timeLimitValue=null;
            if(sendeeState!=null){
                String timeLimit=sendeeState.getTimeLimit();
                if(timeLimit!=null&&!timeLimit.equals("")){
                    if(!StringUtils.isNumeric(timeLimit)){
                        String refVarName="";
                        String expressFunc="function expressFunc(){"+timeLimit+"}";
                        String[] entityLinkArray=entityLink.split("/");
                        if(entityLinkArray.length>=2){
                            refVarName=entityLinkArray[entityLinkArray.length-2]+"Ref";
                            //JsonObject entityObj=getEntityObj(entityLink,tokenName,token);//注入对象不能转成JsonObject 不然会导致表达式不能正确得到结果
                            Object refObject=getEntityObject(entityLink,tokenName,token);//注入对象要用返回的原始对象
                            simpleRulesEngine.injectObjects(new SimpleRulesEngine.InjectedObject(refObject, refVarName));
                            simpleRulesEngine.executeRule(expressFunc);
                            Object exeRet = simpleRulesEngine.executeRule("expressFunc();");
                            if(exeRet!=null){
                                timeLimitValue=Long.parseLong(exeRet.toString());
                            }
                        }
                    }else{
                        timeLimitValue=Long.parseLong(timeLimit);
                    }
             }
            }


            // 判断代表模式并处理 start
            // 代表所有
            if (action.getDelegateMode() == DelegateModeEnum.DELEGATE_ALL) {
                // 将同一实例中其他所有 todo 设为完成
                List<Todo> todos = todoDao.findByInstance(todo.getInstance());
                for (Todo td : todos) {
                    if (td != todo && !td.getCompleted()) {
                        td.setCompleted(true);
                        todoDao.save(td);
                        completeTodoLink(td.getId(), tokenName, token);
                    }
                }
                //如果配置了结束接受者当前实例原有的待办，就结束接受者原有的待办
                if(action.getCompleteReceiverOldToDos()!=null&&action.getCompleteReceiverOldToDos()){
                    completeToDosByInstanceAndExecutors(todo.getInstance(),sendeeList,tokenName,token);
                }
                for (JsonElement sendee : sendeeList) {
                    // 为接收人创建待办事项
                    Todo sendeeTodo = createTodo(todo.getInstance(), action.getSendeeState(), creator, executor,
                            sendee.getAsJsonObject(), batchNumber, todoTitle, todoCategory, todoEmergency, tokenName, token,timeLimitValue);

                    // 创建流转记录
                    createHistory(todo, executor, action, sendee.getAsJsonObject(), sendeeTodo);
                }
            }
            // 代表同一批次
            else if (action.getDelegateMode() == DelegateModeEnum.DELEGATE_SAME_BATCH) {
                // 查找同一批次 todo 并设为完成
                List<Todo> todos = todoDao.findByBatchNumber(todo.getBatchNumber());
                for (Todo td : todos) {
                    if (td != todo && !td.getCompleted()) {
                        td.setCompleted(true);
                        todoDao.save(td);
                        completeTodoLink(td.getId(), tokenName, token);
                    }
                }
                //如果配置了结束接受者当前实例原有的待办，就结束接受者原有的待办
                if(action.getCompleteReceiverOldToDos()!=null&&action.getCompleteReceiverOldToDos()==true){
                    completeToDosByInstanceAndExecutors(todo.getInstance(),sendeeList,tokenName,token);
                }
                for (JsonElement sendee : sendeeList) {
                    // 为接收人创建待办事项
                    Todo sendeeTodo = createTodo(todo.getInstance(), action.getSendeeState(), creator, executor,
                            sendee.getAsJsonObject(), batchNumber, todoTitle, todoCategory, todoEmergency, tokenName, token,timeLimitValue);

                    // 创建流转记录
                    createHistory(todo, executor, action, sendee.getAsJsonObject(), sendeeTodo);
                }
            }
            // 判断代表模式并处理 end

            // 判断等待模式并处理 start
            // 等待所有
            if (action.getWaitMode() == WaitModeEnum.WAIT_ALL) {
                // 查找相同实例中所有待办
                List<Todo> todos = todoDao.findByInstance(todo.getInstance());
                // 如果当前 todo 是最后一个未完成的 todo，才真正为接收人创建待办事项
                List<Todo> todoList = Queryable.from(todos).filter(t -> !t.getCompleted()).toList();
                if (todoList.size() == 1 && todoList.get(0).getId().equals(todo.getId())) {
                    //如果配置了结束接受者当前实例原有的待办，就结束接受者原有的待办
                    if(action.getCompleteReceiverOldToDos()!=null&&action.getCompleteReceiverOldToDos()==true){
                        completeToDosByInstanceAndExecutors(todo.getInstance(),sendeeList,tokenName,token);
                    }
                    for (JsonElement sendee : sendeeList) {
                        // 为接收人创建待办事项
                        Todo sendeeTodo = createTodo(todo.getInstance(), action.getSendeeState(), creator, executor, sendee.getAsJsonObject(),
                                batchNumber, todoTitle, todoCategory, todoEmergency, tokenName, token,timeLimitValue);

                        // 创建流转记录
                        createHistory(todo, executor, action, sendee.getAsJsonObject(), sendeeTodo);
                    }
                }else{
                    for (JsonElement sendee : sendeeList) {
                        // 创建流转记录
                        createHistory(todo, executor, action, sendee.getAsJsonObject(), null);
                    }
                }
            }
            // 等待同一批次
            else if (action.getWaitMode() == WaitModeEnum.WAIT_SAME_BATCH) {
                // 查找所有同一批次的待办
                List<Todo> todos = todoDao.findByBatchNumber(todo.getBatchNumber());
                // 如果当前 todo 是最后一个未完成的 todo，才真正为接收人创建待办事项
                List<Todo> todoList = Queryable.from(todos).filter(t -> !t.getCompleted()).toList();
                if (todoList.size() == 1 && todoList.get(0).getId().equals(todo.getId())) {
                    //如果配置了结束接受者当前实例原有的待办，就结束接受者原有的待办
                    if(action.getCompleteReceiverOldToDos()!=null&&action.getCompleteReceiverOldToDos()==true){
                        completeToDosByInstanceAndExecutors(todo.getInstance(),sendeeList,tokenName,token);
                    }
                    for (JsonElement sendee : sendeeList) {
                        // 为接收人创建待办事项
                        Todo sendeeTodo = createTodo(todo.getInstance(), action.getSendeeState(), creator, executor,
                                sendee.getAsJsonObject(), batchNumber, todoTitle, todoCategory, todoEmergency, tokenName, token,timeLimitValue);

                        // 创建流转记录
                        createHistory(todo, executor, action, sendee.getAsJsonObject(), sendeeTodo);
                    }
                }else{
                    for (JsonElement sendee : sendeeList) {
                        // 创建流转记录
                        createHistory(todo, executor, action, sendee.getAsJsonObject(), null);
                    }
                }
            }
            // 判断等待模式并处理 end

            // 不代表不等待的情况
            if (action.getDelegateMode() == DelegateModeEnum.NOT_DELEGATE
                    && action.getWaitMode() == WaitModeEnum.NOT_WAIT) {
                //如果配置了结束接受者当前实例原有的待办，就结束接受者原有的待办
                if(action.getCompleteReceiverOldToDos()!=null&&action.getCompleteReceiverOldToDos()==true){
                    completeToDosByInstanceAndExecutors(todo.getInstance(),sendeeList,tokenName,token);
                }
                for (JsonElement sendee : sendeeList) {

                    // 为接收人创建待办事项
                    Todo sendeeTodo = createTodo(todo.getInstance(), action.getSendeeState(), creator, executor,
                            sendee.getAsJsonObject(), batchNumber, todoTitle, todoCategory, todoEmergency, tokenName, token,timeLimitValue);

                    // 创建流转记录
                    createHistory(todo, executor, action, sendee.getAsJsonObject(), sendeeTodo);
                }
            }

            // 判断是否保持为当前批阅人
            // 如果操作定义 keepAsExecutor 为false,即：不保持为当前批阅人，则将本人的待办设为完成
            if (!action.getKeepAsExecutor()) {
                todo.setCompleted(true);
                todoDao.save(todo);
                //将待办链接移入已办
                completeTodoLink(todo.getId(), tokenName, token);
            }
            return true;
        }catch (Exception ex){
           ex.printStackTrace();
            return false;
        }
    }

    //通过流程实例和接收人结束待办
    public void completeToDosByInstanceAndExecutors(Instance instance,JsonArray sendeeList,
                                                   String tokenName,String token){
        for (JsonElement executor:sendeeList){
            List<Todo> toDos = todoDao.findByExecutorIdAndInstanceAndCompleted(executor.getAsJsonObject().get("id").getAsString(),
                    instance, false);
            for(Todo toDo:toDos){
                toDo.setCompleted(true);
                todoDao.save(toDo);
                //将待办链接移入已办
                completeTodoLink(toDo.getId(), tokenName, token);
            }
        }
    }

    /**
     * 为用户创建待办事项
     *
     * @param instance      所属实例
     * @param state         发送人执行的状态
     * @param creator       原始作者
     * @param sender        发送人
     * @param sendee        接收人
     * @param batchNumber   批次号
     * @param todoTitle     待办标题
     * @param todoCategory  待办分类
     * @param todoEmergency 待办优先级
     */
    private Todo createTodo(Instance instance,
                            State state,
                            JsonObject creator,
                            JsonObject sender,
                            JsonObject sendee,
                            String batchNumber,
                            String todoTitle,
                            String todoCategory,
                            String todoEmergency,
                            String tokenName,
                            String token,
                            Long timeLimit) {
        Todo todo = new Todo();
        todo.setCompleted(false);
        todo.setExecutorId(sendee.get("id").getAsString());
        todo.setExecutorName(sendee.get("name").getAsString());
        if (sendee.get(xpnPlatformConfig.getUser().getOrgNameFild()) != null) {
            String fullName = sendee.get("name").getAsString() + "/"
                    + sendee.get(xpnPlatformConfig.getUser().getOrgNameFild()).getAsString();
            todo.setExecutorName(fullName);
        }
        todo.setInstance(instance);
        todo.setState(state);
        //处理待办办理时限 zyk 20220824
        Date now=new Date();
        todo.setCreatedTime(now);
        if(timeLimit!=null){
            Date deadLine=new Date(now.getTime()+timeLimit*1000);
            todo.setTimeLimit(timeLimit);
            todo.setDeadLine(deadLine);
        }
        todo.setBatchNumber(batchNumber);
        todoDao.save(todo);
        //endOf处理待办办理时限 zyk 20220824

        //调用统一待办接口产生待办链接
        if (!(todoCategory == null || todoCategory.isEmpty())) {
            Map<String, String> map = new HashMap<>(14);
            map.put("taskId", todo.getId().toString());
            map.put("linkUrl", (StringUtils.isNotBlank(instance.getEntityName())?instance.getEntityName():instance.getProcess().getEntityName()) + "?instanceId=" + instance.getId() + "&todoId=" + todo.getId());
            map.put("type", "resource");
            map.put("subject", todoTitle);
            map.put("creatorId", creator.get("id").getAsString());
            map.put("creatorName", creator.get("name").getAsString());
            map.put("creatorOrgId", null);
            map.put("creatorOrgName", creator.get(xpnPlatformConfig.getUser().getOrgNameFild()).getAsString());
            map.put("senderId", sender.get("id").getAsString());
            map.put("senderName", sender.get("name").getAsString());
            map.put("sendeeId", todo.getExecutorId());
            map.put("stateName", todo.getStateName());//状态名称也带到统一待办 张英奎 2022-02-23
            map.put("sentTime", Tool.getDateString(todo.getCreatedTime()));
            map.put("category", todoCategory);
            map.put("categoryCode", instance.getProcess().getCategoryCode());
            map.put("emergency", todoEmergency);
            if(todo.getTimeLimit()!=null){
                map.put("timeLimit", todo.getTimeLimit().toString());
            }
            if(todo.getDeadLine()!=null){
                map.put("deadLine", Tool.getDateString(todo.getDeadLine()));
            }
            HttpHeaders headers = constructHeader(tokenName, token);
            HttpEntity<String> formEntity = new HttpEntity<String>(gson.toJson(map), headers);
            String result = restTemplate.postForObject(xpnPlatformConfig.getTodoLink().getService(), formEntity, String.class);
        }
        return todo;
    }

    /**
     * 创建流转记录并保存
     *
     * @param todo       待办事项
     * @param executor   执行者
     * @param action     操作
     * @param sendee     接收人
     * @param sendeeTodo 接收人的待办
     */
    private void createHistory(Todo todo, JsonObject executor, Action action, JsonObject sendee, Todo sendeeTodo) {
        History history = new History();
        history.setExecutorId(executor.get("id").getAsString());
        history.setExecutorName(executor.get("name").getAsString());
        if (executor.get(xpnPlatformConfig.getUser().getOrgNameFild()) != null) {
            String fullName = executor.get("name").getAsString() + "/"
                    + executor.get(xpnPlatformConfig.getUser().getOrgNameFild()).getAsString();
            history.setExecutorName(fullName);
        }
        if (sendee == null) {
            history.setSendeeId(null);
            history.setSendeeName(null);
        } else {
            history.setSendeeId(sendee.get("id").getAsString());
            history.setSendeeName(sendee.get("name").getAsString());
            if (sendee.get(xpnPlatformConfig.getUser().getOrgNameFild()) != null) {
                String fullName = sendee.get("name").getAsString() + "/"
                        + sendee.get(xpnPlatformConfig.getUser().getOrgNameFild()).getAsString();
                history.setSendeeName(fullName);
            }
        }
        history.setExecutionTodo(todo);
        history.setExecutionStateName(todo.getState().getName());
        history.setInstance(todo.getInstance());
        history.setSendeeTodo(sendeeTodo);
        history.setActionId(action.getId());
        history.setActionName(action.getName());
        historyDao.save(history);
    }

    @Override
    public List<TodoDTO> getTodosByExecutorAndInstance(String executorId, Long instanceId, Long todoId,
                                                       boolean isCompleted, String entityLink, String refVarName, String tokenName, String token) {
        List<TodoDTO> todoDtos = new ArrayList<>();
        Instance instance = instanceDao.getById(instanceId);
        // 如果有todoid，
        if (todoId != null) {
            Todo todo = todoDao.getById(todoId);
            TodoDTO dto = new TodoDTO();
            //是待办才设置待办
            if(!todo.getCompleted()){
                dto.setTodo(todo);
                // 检查操作权限，重新设置操作
                List<Action> actions = checkPermissionOfActions(actionDao.findByStateOrderBySortNo(todo.getState()), entityLink,
                        refVarName, tokenName, token, instance.getStarterId());
                dto.setActions(actions);
                //设置是否可以退回
                if(!todo.getState().getId().equals(instance.getProcess().getInitStateId())){
                    dto.setCanReturnSenderBack(true);
                    //没有配置退回按钮才设置可以退回
                    List<Action> collect = actions.stream().filter(i -> i.getName().contains(goBackString)).collect(Collectors.toList());
                    if (collect.size()==0) {
                        dto.setCanGoBack(true);
                    }
                }
            }else{
                //查询可不可以收回
                List<History> histories = queryTakeBack(todoId);
                if(histories.size()>0){
                    dto.setCanTackBack(true);
                    dto.setTackBackTodoId(todoId);
                }
            }

            // 设置意见栏和意见列表
            dto.setCommentColumns(commentColumnDAO.findByProcessIdOrderBySortNoDesc(instance.getProcess().getId()));
            dto.setComments(commentDAO.findByInstanceId(instance.getId()));
            todoDtos.add(dto);
            //设置原始作者和本人的发送人
            History history = historyDao.findFirstBySendeeTodoId(todo.getId());
            dto.setStarterId(instance.getStarterId());
            if (history != null) {
                dto.setSenderId(history.getExecutorId());
            }
            return todoDtos;
        }

        // 如果没有todoid
        List<Todo> todos = todoDao.findByExecutorIdAndInstanceAndCompleted(executorId, instance, isCompleted);
        ;
        if (todos.size() == 0) {
            TodoDTO dto = new TodoDTO();

            List<Todo> completeTodoList = todoDao.findByExecutorIdAndInstanceAndCompleted(executorId, instance, true);
            for(Todo todo:completeTodoList){
                Long canTackBackId=todo.getId();
                List<History> histories = queryTakeBack(canTackBackId);
                if(histories.size()>0){
                    dto.setCanTackBack(true);
                    dto.setTackBackTodoId(canTackBackId);
                    break;
                }
            }

            // 设置意见栏和意见列表
            dto.setCommentColumns(commentColumnDAO.findByProcessIdOrderBySortNoDesc(instance.getProcess().getId()));
            dto.setComments(commentDAO.findByInstanceId(instance.getId()));
            todoDtos.add(dto);
            return todoDtos;
        } else {
            for (Todo todo : todos) {
                TodoDTO dto = new TodoDTO();
                dto.setTodo(todo);
                // 检查操作权限，重新设置操作
                List<Action> actions = checkPermissionOfActions(actionDao.findByStateOrderBySortNo(todo.getState()), entityLink,
                        refVarName, tokenName, token, instance.getStarterId());
                dto.setActions(actions);

                // 设置意见栏和意见列表
                dto.setCommentColumns(commentColumnDAO.findByProcessIdOrderBySortNoDesc(instance.getProcess().getId()));
                dto.setComments(commentDAO.findByInstanceId(instance.getId()));

                //设置原始作者和本人的发送人
                History history = historyDao.findFirstBySendeeTodoId(todo.getId());
                dto.setStarterId(instance.getStarterId());
                if (history != null) {
                    dto.setSenderId(history.getExecutorId());
                }
                //设置是否可以退回
                if(!todo.getState().getId().equals(instance.getProcess().getInitStateId())){
                    dto.setCanReturnSenderBack(true);
                    //没有配置退回按钮才设置可以退回
                    List<Action> collect = actions.stream().filter(i -> i.getName().contains(goBackString)).collect(Collectors.toList());
                    if (collect.size()==0) {
                        dto.setCanGoBack(true);
                    }
                }
                todoDtos.add(dto);
            }
            return todoDtos;
        }
    }

    @Override
    public List<History> queryTakeBack(Long todoId) {
        // 查找 execution_todo_id 等于 todoId ，
        // 且 sendee_todo_id 对应的待办是激活状态，
        // 且不存在其他 History 的 execution_todo_id 等于本 History 的 sendee_todo_id，
        // 且 action_id 对应的发送配置允许收回。
        // 满足上述条件的 History 列表

        return historyDao.queryTakeBack(todoId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void takeBack(Long historyId, String tokenName, String token) {
        // 查找 History
        History history = historyDao.getById(historyId);
        takeBackByHistory(history,tokenName,token);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void takeBackAllTodo(Long todoId, String tokenName, String token) {
        // 查找 History
        List<History> historyList = historyDao.queryTakeBack(todoId);
        for(History history:historyList){
            takeBackByHistory(history,tokenName,token);
        }
    }

    //通过历史记录执行收回
   public void takeBackByHistory(History history, String tokenName, String token){
       // 将接收人的 todo 设为完成
       Todo sendeeTodo = history.getSendeeTodo();
       if(sendeeTodo!=null){
           sendeeTodo.setCompleted(true);
           todoDao.save(sendeeTodo);

           //删除接收人的待办链接
           String url = xpnPlatformConfig.getTodoLink().getService() + "task/" + sendeeTodo.getId().toString();
           restTemplate.delete(url+"?"+tokenName+"="+token);
       }
       // history 中进行记录，action_id 为空吧，为空代表是收回。因为 action_id 无法进行关联
       History takeBackHistory = new History();
       takeBackHistory.setExecutorId(history.getExecutorId());
       takeBackHistory.setExecutorName(history.getExecutorName());
       takeBackHistory.setSendeeId(history.getExecutorId());
       takeBackHistory.setSendeeName(history.getExecutorName());
       takeBackHistory.setExecutionTodo(history.getExecutionTodo());
       takeBackHistory.setExecutionStateName(history.getExecutionStateName());
       takeBackHistory.setActionName("收回");
       takeBackHistory.setInstance(history.getInstance());
       takeBackHistory.setSendeeTodo(history.getExecutionTodo());
       historyDao.save(takeBackHistory);

       // 最后再将发送人的 todo 设为未完成
       Todo executionTodo = history.getExecutionTodo();
       executionTodo.setCompleted(false);
       todoDao.save(executionTodo);

       //将发送人的已办连接移入待办
       HttpHeaders headersForSend = constructHeader(tokenName, token);
       HttpEntity<String> formEntityForSend = new HttpEntity<String>("", headersForSend);
       String result=restTemplate.postForObject(xpnPlatformConfig.getDoneLink().getService() + "/to_todo/" + executionTodo.getId(), formEntityForSend, String.class);
    }

    @Override
    public List<Action> checkPermissionOfActions(List<Action> actions, String entityLink, String refVarName,
                                                 String tokenName, String token, String startId) {
        if (actions.isEmpty() || actions.size() == 0) {
            return actions;
        }

        //不需要检测的操作列表。
        List<Action> actionsNoPermissionResult = new ArrayList<>();

        CheckPermissionRequest permissondto = new CheckPermissionRequest();
        // 构造规则列表，并添加到参数里面
        List<Rule> rules = new ArrayList<>();
        for (int i = 0; i < actions.size(); i++) {

            // 表单没有配置权限条件，就不用检测了。
            if (actions.get(i).getAuthorizationRule() == null || actions.get(i).getAuthorizationRule().isEmpty()
                    || "".equals((actions.get(i).getAuthorizationRule()))) {
                actionsNoPermissionResult.add(actions.get(i));
            } else {
                Rule rule = new Rule();
                rule.setKey(actions.get(i).getId().toString());
                rule.setRule(actions.get(i).getAuthorizationRule());
                rules.add(rule);
            }
        }
        permissondto.setRules(rules);

        // 构造注入模型参数，添加到参数列表
        if (!refVarName.isEmpty() && !"".equals(refVarName) && !entityLink.isEmpty() && !"".equals(entityLink)) {
            List<InjectedEntity> injectedEntities = new ArrayList<>();
            InjectedEntity injectedEntity = new InjectedEntity();
            injectedEntity.setRefVarName(refVarName);
            injectedEntity.setHref(entityLink + "?" + tokenName + "=" + token + "&projection=workflow");
            injectedEntity.setCreatedByHref(xpnPlatformConfig.getUser().getService() + startId + "?" + tokenName + "=" + token
                    + "&projection=workflow");
            injectedEntities.add(injectedEntity);
            permissondto.setInjectedEntities(injectedEntities);
        }

        // 发送rest请求过滤操作。
        HttpHeaders headers = constructHeader(tokenName, token);
        HttpEntity<String> formEntity = new HttpEntity<String>(gson.toJson(permissondto), headers);
        String result = restTemplate.postForObject(xpnPlatformConfig.getPermisson().getService(), formEntity,
                String.class);

        @SuppressWarnings("serial")
        List<Rule> checkPermissionResult = gson.fromJson(result, new TypeToken<List<Rule>>() {
        }.getType());

        List<Action> actionsResult = new ArrayList<>();
        for (Action action : actions) {
            // 通过有权限的列表来过滤当前列表
            for (int i = 0; i < checkPermissionResult.size(); i++) {
                Long key = Long.parseLong(checkPermissionResult.get(i).getKey());
                if (action.getId().equals(key)) {
                    if (!actionsResult.contains(action)) {
                        actionsResult.add(action);
                    }
                }
            }
            //通过不需要检查权限的列表过滤操作列表
            for (Action actionNoPermission : actionsNoPermissionResult) {
                if (action.getId().equals(actionNoPermission.getId())) {
                    if (!actionsResult.contains(action)) {
                        actionsResult.add(action);
                    }
                }
            }
        }
        return actionsResult;
    }

    @Override
    public List<History> getHistoriesByInstance(Long id) {
        return historyDao.findByInstanceIdOrderByCreatedTime(id);
    }

    @Override
    public Comment getCommentByUserAndInstanceAndTodoAndColumn(String userId, Long instanceId, Long todoId, Long columnId) {
        return commentDAO.findByUserAndInstanceAndTodoAndColumn(userId, instanceId, todoId, columnId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Todo completeTodo(Long todoId, String executorId, Long actionId, String tokenName, String token) {
        // 完成待办
        Todo todo = todoDao.getById(todoId);
        todo.setCompleted(true);
        todoDao.save(todo);

        Action action = actionDao.getById(actionId);

        // 执行人
        JsonObject executor = getUserObj(todo.getExecutorId(), tokenName, token);
        // 创建流转记录
        createHistory(todo, executor, action, null, null);

        //将待办链接移入已办
        completeTodoLink(todo.getId(), tokenName, token);

        // 代表所有
        if (action.getDelegateMode() == DelegateModeEnum.DELEGATE_ALL) {
            // 将同一实例中其他所有 todo 设为完成
            List<Todo> todos = todoDao.findByInstance(todo.getInstance());
            for (Todo td : todos) {
                if (td != todo && !td.getCompleted()) {
                    td.setCompleted(true);
                    todoDao.save(td);
                    completeTodoLink(td.getId(), tokenName, token);
                }
            }
        }

        return todo;
    }

    //已办转待办
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Todo doneToTodo(Long todoId, String executorId, Long actionId, String tokenName, String token){
        Todo todo = todoDao.getById(todoId);
        if(todo !=null){
            todo.setCompleted(false);
            todoDao.save(todo);
            if(actionId != null){
                //sgf自己写
            }
            return todo;
        }
        return null;
    }

    //将待办链接移入已办
    public void completeTodoLink(Long todoId, String tokenName, String token) {
        HttpHeaders headers = constructHeader(tokenName, token);
        HttpEntity<String> formEntity = new HttpEntity<>("", headers);
        String result = restTemplate.postForObject(xpnPlatformConfig.getTodoLink().getService() + "to_done/" + todoId.toString(), formEntity, String.class);
    }

    public JsonObject getUserObj(String userId, String tokenName, String token) {
        // 执行人
        String urlUser = xpnPlatformConfig.getUser().getService() + userId + "?" + tokenName + "=" + token
                + "&projection=workflow";
        String stringExecutor = restTemplate.getForObject(urlUser, String.class);
        JsonObject user = new JsonParser().parse(stringExecutor).getAsJsonObject();
        user.addProperty("id", userId);
        return user;
    }

    public HttpHeaders constructHeader(String tokenName, String token) {
        HttpHeaders headers = new HttpHeaders();
        headers.add(tokenName, token);
        MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
        headers.setContentType(type);
        headers.add("Accept", MediaType.APPLICATION_JSON.toString());
        return headers;
    }

    public JsonObject getEntityObj(String entityLink, String tokenName, String token) {
        String urlEntity = entityLink + "?" + tokenName + "=" + token;
        //特殊处理 zyk 20220629
        URI uri=URI.create(urlEntity);
        String  host=uri.getHost();
        urlEntity=urlEntity.replaceAll(host,"localhost");
        //endOf特殊处理 zyk 20220629
        String stringExecutor = restTemplate.getForObject(urlEntity, String.class);
        JsonObject entity = new JsonParser().parse(stringExecutor).getAsJsonObject();
        return entity;
    }

    public Object getEntityObject(String entityLink, String tokenName, String token) {
        String urlEntity = entityLink + "?" + tokenName + "=" + token;
        //特殊处理 zyk 20220629
        URI uri=URI.create(urlEntity);
        String  host=uri.getHost();
        urlEntity=urlEntity.replaceAll(host,"localhost");
        //endOf特殊处理 zyk 20220629
        Object entity = restTemplate.getForObject(urlEntity, Object.class);
        return entity;
    }


    public String getInfoInEntityObj(JsonObject entityObj, JsonElement rule) {
        if (rule==null || rule.isJsonNull())
            return "";
        //不是数组直接返回
        if (!rule.isJsonArray()) return rule.getAsString();
        //空数组，返回空
        JsonArray ruleArray=rule.getAsJsonArray();
        if (ruleArray.size()==0) return "";

        StringBuffer buffer = new StringBuffer();
        for (JsonElement item:ruleArray){
            JsonElement keyElement=item.getAsJsonObject().get("key");
            if(keyElement==null||keyElement.isJsonNull())
                continue;
            //获取单个对象的key
            String key=keyElement.getAsString();
            if(key.equals(""))
                continue;
            String[] keyArray=key.split("\\.");
            String subRuleValue="";
            JsonElement resultObj = null;
            for (String subKey : keyArray) {
                if (resultObj == null) {
                    resultObj = entityObj.get(subKey);
                } else {
                    if (resultObj.isJsonObject())
                        resultObj = resultObj.getAsJsonObject().get(subKey);
                }
            }
            if(resultObj!=null&&!resultObj.isJsonNull())
                subRuleValue=resultObj.getAsString();
            //如果有替换模板，就把计算出来的只用替换模板替换
            JsonElement replaceTempleteObj=item.getAsJsonObject().get("replaceTemplete");
            if(!subRuleValue.equals("")&&replaceTempleteObj!=null&&!replaceTempleteObj.isJsonNull()){
                subRuleValue=replaceTempleteObj.getAsString().replaceAll("k_e_y",subRuleValue);
            }
            if(!subRuleValue.equals("")){
                buffer.append(subRuleValue);
            }
        }
        return buffer.toString();
    }

    /**
     * 创建新流程具体实现
     *
     * @param process   流程定义
     * @param starterId 启动者Id
     * @return 创建成功的待办
     */
    private Todo startProcess(Process process, String starterId, String tokenName, String token, String entityLink, String refVarName) {
        // 检查是否具备创建流程的权限
        List<Action> actions = new ArrayList<>();
        Action action = new Action();
        action.setId(process.getId());
        action.setAuthorizationRule(process.getStartAuthorizationRule());
        actions.add(action);
        actions = checkPermissionOfActions(actions, entityLink, refVarName, tokenName, token, "");
        if (actions.isEmpty() || actions.size() == 0) {
            return null;
        }

        // 创建流程实例
        Instance instance = new Instance();
        instance.setStarterId(starterId);
        instance.setProcess(process);
        instanceDao.save(instance);

        // 创建待办
        // 执行人
        JsonObject executor = getUserObj(starterId, tokenName, token);
        Todo todo = createTodo(instance, stateDao.getById(process.getInitStateId()), null, null,
                executor, java.util.UUID.randomUUID().toString(), null, null, null, tokenName, token,null);
        todoDao.save(todo);

        return todo;
    }
    /**
     * 创建新流程具体实现 zyk 2017-12-29
     *
     * @param process   流程定义
     * @param starterId 启动者Id
     * @param state 从指定某环节启动
     * @return 创建成功的待办
     */
    private Todo startProcess(Process process,State state, String starterId, String tokenName, String token, String entityLink, String refVarName) {
        // 检查是否具备创建流程的权限
        List<Action> actions = new ArrayList<>();
        Action action = new Action();
        action.setId(process.getId());
        action.setAuthorizationRule(process.getStartAuthorizationRule());
        actions.add(action);
        actions = checkPermissionOfActions(actions, entityLink, refVarName, tokenName, token, "");
        if (actions.isEmpty() || actions.size() == 0) {
            return null;
        }

        // 创建流程实例
        Instance instance = new Instance();
        instance.setStarterId(starterId);
        instance.setProcess(process);
        instanceDao.save(instance);

        // 创建待办
        // 执行人
        JsonObject executor = getUserObj(starterId, tokenName, token);
        State initState=null;
        if(state!=null) {
        	initState=state;
        }else {
        	initState=stateDao.getById(process.getInitStateId());
        }
        Todo todo = createTodo(instance, initState, null, null,
                executor, java.util.UUID.randomUUID().toString(), null, null, null, tokenName, token,null);
        todoDao.save(todo);

        return todo;
    }

    /**
     * 通过代办事项id获取可以退回的人员信息。
     * @param todoId
     * @return
     */
    @Override
    public List<UserDto> getGoBackUser(Long todoId) {
        Todo todo = todoDao.getById(todoId);
        List<UserDto> returnData = new ArrayList<>();
        //根据代办往前找出所有的历史历史记录
        History history = historyDao.findFirstBySendeeTodoId(todoId);
        if (history==null) {
            return returnData;
        }
        while (history!=null){
            Todo executionTodo = history.getExecutionTodo();
            if (executionTodo==null) {
                history = null;
                continue;
            }
            //如果添加到某个状态，和当前代办所在的状态时同一个状态，说明流程都已经走到后面的环节然后退回来了，不管后面流程退了多少次，都只获取当前状态往前的状态
            if (executionTodo.getState().getId().equals(todo.getState().getId())) {
                //和当前代办所在的状态时同一个状态,清空数组。重新往前添加。
                returnData = new ArrayList<>();
            }else{
                UserDto dto = constructUserDto(history);
                //如果当前要加入的状态在原来的集合里面已经存在，说明前面的流程存在来回退回，那就需要把多余的清除掉（不添加多余的流程状态），
                List<UserDto> collect = returnData.stream().filter(i -> i.getSendeeStateId().equals(dto.getSendeeStateId())).collect(Collectors.toList());
                if (collect.size()==0) {
                    returnData.add(dto);
                }
            }
            history = historyDao.findFirstBySendeeTodoId(executionTodo.getId());
        }
        return returnData;
    }

    private UserDto constructUserDto(History history){
        Todo executionTodo = history.getExecutionTodo();
        UserDto dto = new UserDto();
        dto.setId(history.getExecutorId());
        String executorName = history.getExecutorName();
        if (StringUtils.isNotBlank(executorName)) {
            String[] split = history.getExecutorName().split("/");
            dto.setName(split[0]);
            if (split.length>1) {
                dto.setName(split[0]);
                dto.setOrgFullName(executorName.replaceAll(dto.getName()+"/",""));
            }
        }

        dto.setSendeeStateId(executionTodo.getState().getId());
        dto.setSendeeStateName(executionTodo.getState().getName());
        return dto;
    }

    @Override
    public List<UserDto> returnSenderUser(Long todoId) {
        List<UserDto> returnData = new ArrayList<>();
        //根据代办往前找出所有的历史历史记录
        History history = historyDao.findFirstBySendeeTodoId(todoId);
        if (history==null) {
            return returnData;
        }
        UserDto dto = constructUserDto(history);
        returnData.add(dto);
        return returnData;
    }
}
